package com.investmenttechnologyservices.util;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Transaction;
import com.investmenttechnologyservices.datadictionary.FieldType;

/**
 * This is the utility class for all servlets. It provides method for inserting,
 * deleting, searching the entity from data store. Also contains method for
 * displaying the entity in JSON format.
 * 
 */
public class Util {

	private static final Logger logger = Logger.getLogger(Util.class
			.getCanonicalName());
	private static DatastoreService datastore = DatastoreServiceFactory
			.getDatastoreService();

	/**
	 * 
	 * @param entity
	 *            : entity to be persisted
	 */
	public static void persistEntity(Entity entity) {
		logger.log(Level.INFO, "Saving entity");
		Transaction txn = datastore.beginTransaction();
		try {
			datastore.put(entity);
			txn.commit();
		} finally {
			if (txn.isActive()) {
				txn.rollback();
			}
		}
	}

	/**
	 * Search and return the entity from the cache . If absent , search the
	 * datastore.
	 * 
	 * @param key
	 *            : key to find the entity
	 * @return entity
	 */
	public static Entity findEntity(Key key) {
		logger.log(Level.INFO, "Search the entity");
		try {
			return datastore.get(key);
		} catch (EntityNotFoundException e) {
			return null;
		}
	}

	/***
	 * Search entities based on search criteria
	 * 
	 * @param kind
	 * @param searchBy
	 *            : Searching Criteria (Property)
	 * @param searchFor
	 *            : Searching Value (Property Value)
	 * @return List all entities of a kind from the cache or datastore (if not
	 *         in cache) with the specified properties
	 */
	public static Iterable<Entity> listEntities(String kind, String searchBy, Object searchFor) {
		logger.log(Level.INFO, "Search entities based on search criteria");
		Query q = new Query(kind);
		if (searchFor != null && !"".equals(searchFor)) {
			q.setFilter(new Query.FilterPredicate(searchBy, FilterOperator.EQUAL, searchFor));
		}
		PreparedQuery pq = datastore.prepare(q);
		logger.log(Level.WARNING, pq.toString());
		return pq.asIterable();
	}

	/**
	 * List the entities in JSON format
	 * 
	 * @param entities
	 *            entities to return as JSON strings
	 */
	public static String writeJSON(Iterable<Entity> entities) {
		logger.log(Level.INFO, "creating JSON format object");
		StringBuilder sb = new StringBuilder();
		int i = 0;
		sb.append("{\"data\": [");
		for (Entity result : entities) {
			Map<String, Object> properties = result.getProperties();
			sb.append("{");
			if (result.getKey().getName() == null)
				sb.append("\"name\" : \"" + result.getKey().getId() + "\",");
			else
				sb.append("\"name\" : \"" + result.getKey().getName() + "\",");

			for (String key : properties.keySet()) {
				sb.append("\"" + key + "\" : \"" + properties.get(key) + "\",");
			}
			sb.deleteCharAt(sb.lastIndexOf(","));
			sb.append("},");
			i++;
		}
		if (i > 0) {
			sb.deleteCharAt(sb.lastIndexOf(","));
		}
		sb.append("]}");
		return sb.toString();
	}

	/**
	 * Utility method to send the error back to UI
	 * 
	 * @param data
	 * @param resp
	 * @throws IOException
	 */
	public static String getErrorResponse(Exception ex) throws IOException {
		return "Error:" + ex.toString();
	}

	public static void printEntity(Entity entity) {
		System.err.print("Entity/key="+entity.getKey()+"/");
		Map<String,Object> map = entity.getProperties();
		for (Entry<String,Object> entry : map.entrySet()) {
			System.err.print(entry.getKey()+"="+entry.getValue()+"/");
		}
		System.err.println();
	}

	public static void enumPicker(Class<? extends Enum<?>> enumClass, HttpServletRequest req, HttpServletResponse res) throws ServletException {
		try {
			PrintWriter w = res.getWriter();
			
			w.append('[');

			boolean first = true;
			Enum<?>[] enumConstants = enumClass.getEnumConstants();
			for (Enum<?> enum1 : enumConstants) {
				String name = enum1.name();
				if (!first) {
					w.append(',');
				}
				
				w.append('{');
				printJSON(w, "id", FieldType.STRING, name, true);
				w.append('}');
				
				first = false;
			}

			w.append(']');
			
		} catch (Exception ex) {
			throw new ServletException(ex);
		}
	}

	public static void printJSON(Writer w, String name, FieldType fieldType, Object value, boolean first) throws IOException {
		if (!first) {
			w.append(',');
		}
		
		w.append('\"');
		w.append(name);
		w.append('\"');
		w.append(':');
		
		String strValue;
		if (value == null) {
			strValue = null;
		} else {
			strValue = fieldType.valueToString(value);
		}
		
		w.append(strValue);
	}
	
	
}